package com.hx.setting.dao;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.dao.support.DaoSupport;
import org.springframework.util.Assert;

/**
 * Generate Date : 2014-03-16
 * Generate Version : v3.0
 * Generate Name : Honey v3.1 Test
 * Generate User : THINK
 * JDK Version : 1.5
 */
public abstract class AbstractJDBCDaoSupport extends DaoSupport
{

	private volatile boolean			pmdKnownBroken	= false;

	private DataSource					dataSource;

	private static final Set<String>	PRIMITIVE_NAMES	= new java.util.HashSet<String>();
	static
	{
		PRIMITIVE_NAMES.add( "boolean" );
		PRIMITIVE_NAMES.add( Boolean.class.getName() );
		PRIMITIVE_NAMES.add( "byte" );
		PRIMITIVE_NAMES.add( Byte.class.getName() );
		PRIMITIVE_NAMES.add( "char" );
		PRIMITIVE_NAMES.add( Character.class.getName() );
		PRIMITIVE_NAMES.add( "short" );
		PRIMITIVE_NAMES.add( Short.class.getName() );
		PRIMITIVE_NAMES.add( "int" );
		PRIMITIVE_NAMES.add( Integer.class.getName() );
		PRIMITIVE_NAMES.add( "long" );
		PRIMITIVE_NAMES.add( Long.class.getName() );
		PRIMITIVE_NAMES.add( "float" );
		PRIMITIVE_NAMES.add( Float.class.getName() );
		PRIMITIVE_NAMES.add( "double" );
		PRIMITIVE_NAMES.add( Double.class.getName() );
		PRIMITIVE_NAMES.add( "void" );
		PRIMITIVE_NAMES.add( Void.class.getName() );
		PRIMITIVE_NAMES.add( java.util.Date.class.getName() );
		PRIMITIVE_NAMES.add( java.sql.Date.class.getName() );
	}

	public void setDataSource(DataSource dataSource)
	{
		this.dataSource = dataSource;
	}

	public boolean isPmdKnownBroken()
	{
		return pmdKnownBroken;
	}

	public void setPmdKnownBroken(boolean pmdKnownBroken)
	{
		this.pmdKnownBroken = pmdKnownBroken;
	}

	public DataSource getDataSource()
	{
		return dataSource;
	}

	/** 
	 * 获取PreparedStatement
	 * @param connection 数据库连接
	 * @param sql	sql语句
	 * @return 返回PreparedStatement对象
	 * @throws SQLException
	 */
	protected PreparedStatement prepareStatement(Connection connection, String sql) throws SQLException
	{
		return connection.prepareStatement( sql );
	}

	/** 
	 * 获取数据库连接
	 * @return Connection对象
	 * @throws SQLException
	 */
	protected Connection prepareConnection() throws SQLException
	{
		if ( this.getDataSource() == null )
		{
			throw new SQLException( "QueryRunner requires a DataSource to be " + "invoked in this way, or a Connection should be passed in" );
		}
		return this.dataSource.getConnection();
	}

	/** 
	 * 使用数组给PreparedStatement对象填充数据
	 * @param preparedStatement
	 * @param parameters 可变参数
	 * @throws SQLException
	 */
	public void fillStatement(PreparedStatement preparedStatement, Object[] parameters) throws SQLException
	{
		// check the parameter count, if we can
		ParameterMetaData pmd = null;
		if ( !pmdKnownBroken )
		{
			pmd = preparedStatement.getParameterMetaData();
			int stmtCount = pmd.getParameterCount();
			int paramsCount = parameters == null ? 0 : parameters.length;
			if ( stmtCount != paramsCount )
			{
				throw new SQLException( "Wrong number of parameters: expected " + stmtCount + ", was given " + paramsCount );
			}
		}
		// nothing to do here
		if ( parameters == null )
		{
			return;
		}
		Object param = null;
		Class<?> clazz = null;
		for ( int i = 0 ; i < parameters.length ; i++ )
		{
			param = parameters[i];
			if ( param != null )
			{
				clazz = param.getClass();
				if ( (clazz.isPrimitive()) || (PRIMITIVE_NAMES.contains( clazz.getName() )) )
				{
					preparedStatement.setObject( i + 1 ,param );
				}
				else if ( param instanceof com.hx.setting.model.EnumStructure )
				{
					com.hx.setting.model.EnumStructure structure = (com.hx.setting.model.EnumStructure) param;
					preparedStatement.setObject( i + 1 ,structure.getValue() );
				}
				else
				{
					preparedStatement.setObject( i + 1 ,param.toString() );
				}
			}
			else
			{
				// VARCHAR works with many drivers regardless
				// of the actual column type. Oddly, NULL and
				// OTHER don't work with Oracle's drivers.
				int sqlType = Types.VARCHAR;
				if ( !pmdKnownBroken )
				{
					try
					{
						/*
						 * It's not possible for pmdKnownBroken to change from
						 * true to false, (once true, always true) so pmd cannot
						 * be null here.
						 */
						sqlType = pmd.getParameterType( i + 1 );
					}
					catch ( SQLException e )
					{
						pmdKnownBroken = true;
					}
				}
				preparedStatement.setNull( i + 1 ,sqlType );
			}
		}
	}

	/** 
	 * 使用java bean 给PreparedStatement对象填充数据
	 * @param preparedStatement
	 * @param bean bean 对象
	 * @param properties
	 * @throws SQLException
	 */
	public void fillStatementWithBean(PreparedStatement preparedStatement, Object bean, PropertyDescriptor[] properties) throws SQLException
	{
		Object[] params = new Object[properties.length];
		for ( int i = 0 ; i < properties.length ; i++ )
		{
			PropertyDescriptor property = properties[i];
			Object value = null;
			Method method = property.getReadMethod();
			if ( method == null )
			{
				throw new RuntimeException( "No read method for bean property " + bean.getClass() + " " + property.getName() );
			}
			try
			{
				value = method.invoke( bean ,new Object[0] );
			}
			catch ( InvocationTargetException e )
			{
				throw new RuntimeException( "Couldn't invoke method: " + method, e );
			}
			catch ( IllegalArgumentException e )
			{
				throw new RuntimeException( "Couldn't invoke method with 0 arguments: " + method, e );
			}
			catch ( IllegalAccessException e )
			{
				throw new RuntimeException( "Couldn't invoke method: " + method, e );
			}
			params[i] = value;
		}
		fillStatement( preparedStatement ,params );
	}

	/** 
	 * 使用java bean 给PreparedStatement对象填充数据
	 * @param preparedStatement
	 * @param bean
	 * @param propertyNames
	 * @throws SQLException
	 */
	public void fillStatementWithBean(PreparedStatement preparedStatement, Object bean, String[] propertyNames) throws SQLException
	{
		PropertyDescriptor[] descriptors;
		try
		{
			descriptors = Introspector.getBeanInfo( bean.getClass() ).getPropertyDescriptors();
		}
		catch ( IntrospectionException e )
		{
			throw new RuntimeException( "Couldn't introspect bean " + bean.getClass().toString(), e );
		}
		PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
		for ( int i = 0 ; i < propertyNames.length ; i++ )
		{
			String propertyName = propertyNames[i];
			if ( propertyName == null )
			{
				throw new NullPointerException( "propertyName can't be null: " + i );
			}
			boolean found = false;
			for ( int j = 0 ; j < descriptors.length ; j++ )
			{
				PropertyDescriptor descriptor = descriptors[j];
				if ( propertyName.equals( descriptor.getName() ) )
				{
					sorted[i] = descriptor;
					found = true;
					break;
				}
			}
			if ( !found )
			{
				throw new RuntimeException( "Couldn't find bean property: " + bean.getClass() + " " + propertyName );
			}
		}
		fillStatementWithBean( preparedStatement ,bean ,sorted );
	}

	/** 
	 * 重新定向sql异常
	 * @param cause
	 * @param sql
	 * @param parameters
	 * @throws SQLException
	 */
	protected void rethrow(SQLException cause, String sql, Object[] parameters) throws SQLException
	{
		String causeMessage = cause.getMessage();
		if ( causeMessage == null )
		{
			causeMessage = "";
		}
		StringBuffer msg = new StringBuffer( causeMessage );
		msg.append( " Query: " );
		msg.append( sql );
		msg.append( " Parameters: " );
		if ( parameters == null )
		{
			msg.append( "[]" );
		}
		else
		{
			msg.append( Arrays.deepToString( parameters ) );
		}
		SQLException e = new SQLException( msg.toString(), cause.getSQLState(), cause.getErrorCode() );
		e.setNextException( cause );
		throw e;
	}

	/** 
	 * 关闭连接
	 * @param connection
	 * @throws SQLException
	 */
	protected void close(Connection connection) throws SQLException
	{
		if ( connection != null )
		{
			connection.close();
		}
	}

	/** 
	 * 关闭statement
	 * @param statement
	 * @throws SQLException
	 */
	protected void close(Statement statement) throws SQLException
	{
		if ( statement != null )
		{
			statement.close();
		}
	}

	/** 
	 * 关闭resultSet
	 * @param resultSet
	 * @throws SQLException
	 */
	protected void close(ResultSet resultSet) throws SQLException
	{
		if ( resultSet != null )
		{
			resultSet.close();
		}
	}

	@Deprecated
	protected void checkDaoConfig()
	{
		Assert.notNull( this.dataSource ,"Property 'dataSource' are required" );
	}

	/** 
	 * 批量提交
	 * @param conn 数据库连接
	 * @param closeConn 是否关闭数据连接
	 * @param sql 查询语句
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	protected final int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException
	{
		int[] answer = null;
		if ( conn == null )
		{
			throw new SQLException( "Null connection" );
		}
		if ( sql == null )
		{
			if ( closeConn )
			{
				close( conn );
			}
			throw new SQLException( "Null SQL statement" );
		}
		if ( params == null )
		{
			if ( closeConn )
			{
				close( conn );
			}
			throw new SQLException( "Null parameters. If parameters aren't need, pass an empty array." );
		}
		PreparedStatement stmt = null;
		try
		{
			stmt = this.prepareStatement( conn ,sql );
			for ( int i = 0 ; i < params.length ; i++ )
			{
				this.fillStatement( stmt ,params[i] );
				stmt.addBatch();
			}
			answer = stmt.executeBatch();
		}
		catch ( SQLException e )
		{
			this.rethrow( e ,sql ,(Object[]) params );
		}
		finally
		{
			close( stmt );
			if ( closeConn )
			{
				close( conn );
			}
		}
		return answer;
	}

	/** 
	 * 查询
	 * @param conn 数据库连接
	 * @param closeConn 是否关闭数据连接
	 * @param hasBlob 是否包含大字段
	 * @param sql 查询语句
	 * @param rsh 结果集处理句柄
	 * @param params 参数
	 * @return 返回查询结果
	 * @throws SQLException
	 */
	protected final Object query(Connection conn, boolean closeConn, boolean hasBlob, String sql, ResultSetHandler rsh, Object[] params) throws SQLException
	{
		Object answer = null;
		if ( conn == null )
		{
			throw new SQLException( "Null connection" );
		}
		if ( sql == null )
		{
			if ( closeConn )
			{
				close( conn );
			}
			throw new SQLException( "Null SQL statement" );
		}
		if ( rsh == null )
		{
			if ( closeConn )
			{
				close( conn );
			}
			throw new SQLException( "Null ResultSetHandler" );
		}
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try
		{
			stmt = this.prepareStatement( conn ,sql );
			this.fillStatement( stmt ,params );
			rs = stmt.executeQuery();
			answer = rsh.handle( rs ,hasBlob );
		}
		catch ( SQLException e )
		{
			this.rethrow( e ,sql ,params );
		}
		finally
		{
			try
			{
				close( rs );
			}
			finally
			{
				close( stmt );
				if ( closeConn )
				{
					close( conn );
				}
			}
		}
		return answer;
	}

	/** 
	 * 执行更新语句
	 * @param conn　数据库连接
	 * @param closeConn　是否关闭数据连接
	 * @param sql　数据语句
	 * @param rsh 结果集处理句柄
	 * @param params　参数
	 * @return　返回影响的记录集数量
	 * @throws SQLException　
	 */
	protected final int update(Connection conn, boolean closeConn, String sql, ResultSetHandler rsh, Object[] params) throws SQLException
	{
		int answer = 0;
		if ( conn == null )
		{
			throw new SQLException( "Null connection" );
		}
		if ( sql == null )
		{
			if ( closeConn )
			{
				close( conn );
			}
			throw new SQLException( "Null SQL statement" );
		}
		PreparedStatement stmt = null;
		try
		{
			stmt = this.prepareStatement( conn ,sql );
			this.fillStatement( stmt ,params );
			answer = stmt.executeUpdate();
			if ( rsh != null )
			{
				ResultSet rs = stmt.getGeneratedKeys();
				rsh.handle( rs ,false );
				close( rs );
			}
		}
		catch ( SQLException e )
		{
			this.rethrow( e ,sql ,params );
		}
		finally
		{
			close( stmt );
			if ( closeConn )
			{
				close( conn );
			}
		}
		return answer;
	}

	/**
	 * ResultSet处理句柄
	 * @author THINK
	 * 
	 * @param <T>
	 */
	public interface ResultSetHandler<T>
	{

		T handle(ResultSet resultSet, boolean hasBlob) throws SQLException;
	}

	/**
	 * 
	 * @author THINK
	 * 
	 */
	public class DefaultResultSetHandler implements ResultSetHandler
	{

		@Override
		public Object handle(ResultSet resultSet, boolean hasBlob) throws SQLException
		{
			return null;
		}
	}

	public class GeneratedKeysHandler implements ResultSetHandler
	{

		private Long	generatedKeys	= null;

		@Override
		public Object handle(ResultSet resultSet, boolean hasBlob) throws SQLException
		{
			if ( resultSet.next() )
			{
				generatedKeys = resultSet.getLong( 1 );
			}
			return null;
		}

		public Long getGeneratedKeys()
		{
			return generatedKeys;
		}
	}
}
/* Honey v3.1 Test - version:1 - OriginalChecksum:B1747bae56fd3f8b97ba75519d747ebf8 (do not edit this line) */
